package org.com.yz.dubbo.service.impl;

import cn.hutool.core.date.DateTime;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSONObject;
import org.com.yz.common.config.ConfigureTest;
import org.com.yz.common.config.IPostService;
import org.com.yz.common.config.IRequestService;
import org.com.yz.common.config.ServiceName;
import org.com.yz.common.util.*;
import org.com.yz.dal.dao.mapper.AuthenticationDetailMapper;
import org.com.yz.dal.dao.mapper.BankCardMapper;
import org.com.yz.dal.dao.mapper.DepositTransactionMapper;
import org.com.yz.dal.dao.model.*;
import org.com.yz.dubbo.api.service.IBankCardService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.tools.Tool;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * <p>
 * 银行卡信息表 服务实现类
 * </p>
 *
 * @author wzc123
 * @since 2018-07-16
 */
@Service(version = "1.0.0", interfaceName = "org.com.yz.dubbo.api.service.IBankCardService")

public class BankCardServiceImpl implements IBankCardService {

    private static Logger log = LoggerFactory.getLogger(BankCardServiceImpl.class);

    @Autowired
    private BankCardMapper bankCardMapper;//银行卡dao
    @Autowired
    private DepositTransactionMapper depositTransactionMapper;//事务dao
    @Autowired
    private AuthenticationDetailMapper authenticationDetailMapper; //鉴权明细dao
    @Autowired
    private AsyncTask asyncTask;//异步处理类


    /**
     * @param bankCard, mchNo, outTransNo, bizType]
     * @Description: 绑定银行卡(对私)
     * ** 调用收付直通车鉴权接口（银行说法：鉴权是调用成功就收付，只要我们收到请求，就会发送到渠道，就产生费用。这样就包括认证的正常失败，返回状态为2的。
     * 系统维护的时候收到的请求，或者参数有误，都是返回errcode，这种时候没有发往渠道，就不收费
     * 异常报错只有一种情况会产生费用，就是请求成功，返回信息给贵司，贵司解析失败，或者链接超时没有收到，实际上是发往渠道了，这种就会有
     * 超时后可以重试，如果报系统跟踪号重复，说明之前已经提交过了
     * 系统跟踪号是商户号下唯一性，用原来的系统跟踪号查，如果有入库，就能查到结果。如果报订单号不存在，可以重复提交，这样这个订单号也就只有一条记录）
     * ** 如果请求失败，即包含errorcode，直接返回
     * ** 如果请求成功，插入鉴权信息，判断认证状态
     * ***  如果认证状态为失败，则不插入银行卡信息，反之，则插入银行卡信息（如果当即认证成功，后续不会异步通知）
     * @author wzc
     * @date 2018/7/24 17:16
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BankCard bandCardHandle(BankCard bankCard, String mchNo, String outTransNo, String bizType) {
        //1.根据名称或银行卡号匹配卡宾
        String bankName = bankCard.getBankName();
        String bankCardBin = bankCard.getCardNo().substring(0, 6);
        List<BankCardBin> bankCardBinList = bankCardMapper.getBankCardBinByBankName(bankName);
        BankCardBin bankCardBinInfo = bankCardMapper.getBankCardBinByCardBin(bankCardBin);
        if (ToolUtil.isEmpty(bankCardBinList) && ToolUtil.isEmpty(bankCardBinInfo)) {
            bankCard.setStatus("error");
            bankCard.setErrmsg("未找到对应机构，不支持该银行！");
            return bankCard;
        }
        MchConfig config = CacheUtil.me().getMchConfigInfo(mchNo, "0");
        String tracNo = bankCard.getOrderNo();
        //查询商户收付直通车相关配置信息
        bankCard.setTracNo(tracNo);
        //兴业收付直通车鉴权start
        /*Map paramMap = getPostMap(config, tracNo, bankCard, mchNo);
        //发送收付直通车独立鉴权请求
        String result = IPostService.txn(ConfigureTest.isDevEnv() ? ConfigureTest.EP_DEV_API : ConfigureTest.EP_PROD_API, paramMap, mchNo);
        result ="{\n" +
                "\t\"trac_no\":\"P0020181015201017000005\",\n" +
                "\t\"sno\":\"2018101526709619\",\n" +
                "\t\"appid\":\"Q0001469\",\n" +
                "\t\"auth_status\":\"0\",\n" +
                "\t\"sign_type\":\"RSA\",\n" +
                "\t\"mac\":\"SOKeaZSyfHO/1blr0c9JAqtiUyA/RRVZm7jxLkvRuJ7KeMVxW+2dkKLR6flLWLQWoPM0l4Zh04mNNH6+iTUL2Z8VqyKG8pjJvfHj9ejGHAoUT62ANzjrZ0TlG6mvFqshRK/5Gg4XHfEvHkqYAStHPdtEtsv/aQA/IW8MjcDG9KA3KJ1oNYyYqjvKnPMcVecRlqXJmUP9+acnfqjO8m5nxGphb0ADMQ/k91XQrrLovRTGkcQcGmdsWLGJ8hlp2iA1ddIYCMCMtsIDeZ3/yPx2EIPGJNzfv8YkhHk+xJIpWqrrzawudu8V2Ew9IGGEQt/rX5rJFgV4YNefGgqJZNJUlg==\"\n" +
                "}";
        //转换为json
        JSONObject jsonObjecta = (JSONObject) JSONObject.parse(result);
        log.info(XmlFormatter.formatJson(result));

        //代表失败--如果失败，直接返回失败结果，不作任何数据库插入操作
        if (result.contains("errcode")) {
            String errmsg = jsonObjecta.get("errmsg").toString();
            bankCard.setStatus("error");
            bankCard.setErrmsg(errmsg);
            return bankCard;
        }
        //代表请求成功--如果请求成功，因为收费，一致记录到鉴权明细表
        //请求成功后需要看认证状态
        //如果认证失败，则不插入银行卡信息；
        // 如果认证成功、待认证、认证处理中，插入银行卡信息，等待定时器查询
        if (result.contains("appid") && result.contains("auth_status")) {
            if ("2".equals(jsonObjecta.get("auth_status"))) {   //认证失败
                bankCard.setStatus("fail");
                bankCard.setRemark(jsonObjecta.getString("remark"));
            } else {
                if ("1".equals(jsonObjecta.get("auth_status"))) {   //认证成功
                    bankCard.setRemark("绑卡成功");
                    bankCard.setStatus("success");
                }
                if ("0".equals(jsonObjecta.get("auth_status"))) {   //待认证
                    bankCard.setRemark("账户待认证");
                    bankCard.setStatus("to_verify");
                }
                if ("3".equals(jsonObjecta.get("auth_status"))) {   //认证处理中
                    bankCard.setRemark("认证处理中");
                    bankCard.setStatus("to_handle");
                }
                //插入银行卡信息
                insertBankCard(bankCard, mchNo, tracNo, "1", bankCard.getStatus(), bankCard.getRemark(), 0);
            }
            //记录鉴权明细
            insertAuthenticationDetail(bankCard, mchNo, tracNo, bankCard.getRemark(), bankCard.getStatus(), "1","50");
            //插入事物
            insertDeposit(mchNo, outTransNo, bizType);
        }*/
        //兴业收付直通车鉴权end



        Map paramMap = null;//第三方鉴权接口start
        try {
            //2.组装第三方鉴权接口所需参数
            paramMap = getAuthenMap(mchNo, tracNo, bankCard);
        } catch (UnsupportedEncodingException e) {
            log.info("加密解密异常");
            bankCard.setStatus("error");
            bankCard.setErrmsg("加密解密异常");
            return bankCard;
        }
        String url = "https://v.fucongpay.com/api/verify";
        //3.调用第三方鉴权接口
        String result = txn(url, paramMap);
        log.info("对私鉴权返回结果为：" + XmlFormatter.formatJson(result));
        JSONObject jsonObjecta = (JSONObject) JSONObject.parse(result);
        String resultStr = jsonObjecta.getString("result");
        //4.判断鉴权结果（通过/不通过/处理失败）
        if ("2".equals(resultStr)) {
            //处理失败1
            String errmsg = jsonObjecta.get("resMsg").toString();
            bankCard.setStatus("error");
            bankCard.setErrmsg(errmsg);
            return bankCard;
        }
        if (result.contains("errcode")) {
            //处理失败2
            String errmsg = jsonObjecta.get("errmsg").toString();
            bankCard.setStatus("error");
            bankCard.setErrmsg(errmsg);
            return bankCard;
        } else {
            if ("0".equals(resultStr)) {
                //通过
                String resCode = jsonObjecta.getString("resCode");
                if ("0000".equals(resCode)) {
                    bankCard.setRemark("绑卡成功");
                    bankCard.setStatus("success");
                    bankCard.setBankName(jsonObjecta.getString("bankDescription"));
                    //插入银行卡信息
                    insertBankCard(bankCard, mchNo, tracNo, "1", bankCard.getStatus(), bankCard.getRemark(), 0);
                }
            } else if ("1".equals(resultStr)) {
                //不通过
                bankCard.setStatus("fail");
                bankCard.setRemark(jsonObjecta.getString("resMsg"));
            }
            //记录鉴权明细
            insertAuthenticationDetail(bankCard, mchNo, tracNo, bankCard.getRemark(), bankCard.getStatus(), "1", "50");
            //插入事物
            insertDeposit(mchNo, outTransNo, bizType);
        }
        //第三方鉴权end
        return bankCard;

    }


    /**
     * 绑定对公账户
     * ** 根据行名匹配卡宾信息获取行号，如果失败则提示未找到对应机构
     * ** 对公采用小额打款，先插入银行卡信息和鉴权明细信息
     * ** 调用银企直联-超级网银转账接口（银行说法：不需要city城市字段，不需要是否同城，测试后无误）
     * ** 如果请求失败，删除银行卡信息和鉴权明细信息
     * ** 如果请求成功，判断转账状态，如果是pending，则异步查询通知调用方转账成功或失败（成功后更新鉴权明细信息，不更改银行卡信息，等待往账成功后再更新银行卡信息；失败后删除两张表信息）
     *
     * @param bankCard
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public BankCard binkCompanyCard(String mchNo, BankCard bankCard) {
        BankCard bankCardRes = new BankCard();
        String bankName = bankCard.getBankName();//收款人开户行名称。
        String orderNo = bankCard.getOrderNo();//订单号
        String bankNum = "";//行号
        List<BankCardBin> bankCardBinList = bankCardMapper.getBankCardBinByBankName(bankName);//对公根据航名称匹配卡宾信息
        if (ToolUtil.isNotEmpty(bankCardBinList) && ToolUtil.isNotEmpty(bankCardBinList.get(0))) {
            bankNum = bankCardBinList.get(0).getBankNum();
        } else {
            bankCardRes.setStatus("error");
            bankCardRes.setErrmsg("未找到对应机构，不支持该银行！");
            return bankCardRes;
        }
        int amt = (int) (Math.random() * 49 + 1);////随机向账户信息打款0.5以下随机金额
        String url = bankCard.getNotifyUrl();//转账结果通知地址
        String isSameBank = "N"; //默认N 是否同行
        String isSameCity = "N"; //默认N 是否同城
        String cardNo = bankCard.getCardNo();
        String accountName = bankCard.getUserName();//户名
        String city = "";//城市名称（非必输，有的客户获取不到城市名称）
        if ("兴业银行".equals(bankCard.getBankName())) {
            isSameCity = "Y"; //同行
        }
        if (ToolUtil.isNotEmpty(bankCard.getBankBranchName())) {
            city = bankCard.getBankBranchName().substring(0, 2);
        }
        WithdrawPay withdrawPay = null;
        try {
            //3**.超级网银小额打款
            withdrawPay = WithdrawPayUtil.withdrawPay(mchNo, orderNo, isSameBank, isSameCity,
                    cardNo, accountName, bankName, bankNum, city, AmountUtil.convertCent2Dollar(String.valueOf(amt)), "1", "1", "对公小额打款");
            //**4.判断打款结果（失败/成功/未决  失败情况：不支持该业务，不存在付款账号，操作员不对，连接超时等）
            if ("FAIL".equals(withdrawPay.getXferprccode())) {
                //失败
                bankCardRes.setRemark(withdrawPay.getMessage());
                bankCardRes.setStatus("fail");
                BankCard bankCard1 = new BankCard();
                bankCard1.setTracNo(bankCard.getOrderNo());
                bankCardMapper.deleteTracNo(bankCard1);//删除银行卡信息
                AuthenticationDetail authenticationDetail = new AuthenticationDetail();
                authenticationDetail.setOrderNo(orderNo);
                authenticationDetail.setMchNo(mchNo);
                authenticationDetailMapper.delete(authenticationDetail);//删除鉴权明细信息
            } else if ("PAYOUT".equals(withdrawPay.getXferprccode())) {
                //成功
                bankCardRes.setRemark(withdrawPay.getMessage());
                bankCardRes.setStatus("success");

                //**1.插入对公账户信息,对公账户绑定状态为待认定
                insertBankCard(bankCard, mchNo, orderNo, "0", "success", "打款成功", amt);
                //**2.记录鉴权明细
                insertAuthenticationDetail(bankCard, mchNo, orderNo, "打款成功", "success", "0", String.valueOf(amt));



               /* AuthenticationDetail authenticationDetail = new AuthenticationDetail();
                authenticationDetail.setStatus("success");//状态
                authenticationDetail.setRemark("打款成功");
                authenticationDetail.setMchNo(mchNo);//商户号
                authenticationDetail.setOrderNo(orderNo);//订单号/请求追踪号
                authenticationDetail.setMchAccntNo(bankCard.getMchAccntNo());
                authenticationDetailMapper.updateAuthenticationDetail(authenticationDetail);//更新鉴权明细信息*/
            } else if ("PENDING".equals(withdrawPay.getXferprccode())) {
                //未决
                bankCardRes.setRemark(withdrawPay.getMessage());
                bankCardRes.setStatus("to_handle");
                //**5.异步查询转账信息并五分钟一次通知客户，直到返回成功停止，并通知客户转账成功（等待客户调用往账接口）
                try {
                    asyncTask.withDrawPayQuery(bankCard.getMchAccntNo(), withdrawPay, mchNo, bankCard.getCardNo(), url, bankCard);
                } catch (Exception e) {
                    log.info("异步查询转账异常"+ToolUtil.getExceptionMsg(e));
                }
            }
            bankCardRes.setMchAccntNo(bankCard.getMchAccntNo());
            bankCardRes.setCardNo(bankCard.getCardNo());
        } catch (Exception e) {
            log.info("小额打款异常"+ToolUtil.getExceptionMsg(e));
            bankCardRes.setErrmsg("打款失败");
            bankCardRes.setStatus("error");
            return bankCardRes;
        }
        return bankCardRes;
    }


    /**
     * 往账接口
     * ** 对公绑卡在接收到打款成功的异步通知后，调用往账接口（对私绑卡不可调用往账接口）
     * ** 不限制请求次数，请求超时时间设定为7天，暂未开发
     *
     * @return
     */
    public NosTroAccount nostro(Nostro nostro) {
        //往账的时候，一定要是小额打款成功之后
        NosTroAccount nosTroAccount1 = new NosTroAccount();
        //查询接收到的金额是否等于存放的金额，如果相等，则绑定成功，更新银行卡状态，反之则失败
        NosTroAccount nosTroAccount = nostro.getBizContent();
        BankCard bankCard = new BankCard();
        bankCard.setMchNo(nostro.getMchNo());
        bankCard.setMchAccntNo(nosTroAccount.getMchAccntNo());
        bankCard.setCardNo(nosTroAccount.getCardNo());
        List<BankCard> bankCardList = bankCardMapper.selectBankCard(bankCard);
        String amt = "0";
        String orderNo = "";
        if (bankCardList != null && bankCardList.size() > 0) {
            amt = bankCardList.get(0).getVerifyPhoneCode();
            orderNo = bankCardList.get(0).getOrderNo();
        }
        if (ToolUtil.isNotEmpty(orderNo)) {
            //根据订单号、子商户账号、商户号，查询鉴权明细记录表该订单状态是否为成功，如果不成功，则返回，小额打款处理中，不可往账
        }
        if (amt.equals(nosTroAccount.getAmt())) {
            //TODO 往账失效机制，如果N天后第一次往账，则提示失效，并置为失败
            nosTroAccount1.setCode("1");
            nosTroAccount1.setAmt(amt);
            nosTroAccount1.setMessage("success");
            bankCard.setStatus("success");
            bankCard.setRemark("交易成功");
            bankCardMapper.undateNoSuess(bankCard);
        } else {
            nosTroAccount1.setCode("2");
            nosTroAccount1.setMessage("往账金额不正确");
        }
        return nosTroAccount1;
    }


    /**
     * @Description: 解绑银行卡逻辑
     * @author wzc
     * @date 2018/7/20 11:02
     */
    @Override
    public int bandCardUntieHandle(MchsubBindBankCard mchsubBindBankCard) {
        //插入事务表
        DepositTransaction depositTransaction = new DepositTransaction();
        depositTransaction.setMchNo(mchsubBindBankCard.getMchNo());
        depositTransaction.setTransactionNo(UUID.randomUUID().toString().replace("-", "").toLowerCase());//事务号
        depositTransaction.setOutTransNo(mchsubBindBankCard.getOutTransNo());//外部追踪号
        depositTransaction.setIdMchAccntChargeUp(0);
        depositTransaction.setCreateTime(new DateTime());//创建时间
        depositTransaction.setTransactionType(mchsubBindBankCard.getBizType());//事务类型
        depositTransaction.setUpdateTime(new DateTime());//更新时间
        //更新事务表
        depositTransactionMapper.insert(depositTransaction);
        BankCard bankCard = mchsubBindBankCard.getBizContent();
        int result = bankCardMapper.delete(bankCard);
        return result;
    }


    /**
     * 组装收付直通车鉴权请求所需的map
     *
     * @param config   配置信息
     * @param tracNo   订单号/追踪号
     * @param bankCard 银行卡信息
     * @param mchNo    商户号
     */
    private Map getPostMap(MchConfig config, String tracNo, BankCard bankCard, String mchNo) {
        HashMap<String, String> paramMap = new HashMap<>();
        paramMap.put("timestamp", DateUtil.getSeqString());
        paramMap.put("appid", config.getApp_id());
        paramMap.put("service", ServiceName.getQuickSingleAuth());
        paramMap.put("ver", ServiceName.getVer());
        //系统跟踪号
        paramMap.put("trac_no", tracNo);
        //银行卡号
        paramMap.put("card_no", bankCard.getCardNo());
        //银行代码
        paramMap.put("bank_no", bankCard.getBankNo());
        //银行账户类型：0-储蓄卡;1-信用卡
        paramMap.put("acct_type", bankCard.getCardType());
        //信用卡
        if ("1".equals(bankCard.getCardType())) {
            paramMap.put("cvn", bankCard.getCardCvn());
            paramMap.put("expireDate", bankCard.getCardExpireDate());
        }
        //持卡人姓名
        paramMap.put("user_name", bankCard.getUserName());
        //证件类型 0-身份证(目前仅支持身份证)
        paramMap.put("cert_type", "0");
        //身份证号
        paramMap.put("cert_no", bankCard.getCertNo());
        //预留手机号
        paramMap.put("card_phone", bankCard.getCardPhone());
        //验签方式
        paramMap.put("sign_type", ServiceName.getSign_typeRsa());
        //签名
        paramMap.put("mac", Signature.generateMAC(paramMap, mchNo));
        return paramMap;
    }


    /**
     * 组装第三方鉴权接口所需参数
     * @param mchNo 商户号 用于查询配置文件中的key
     * @param tracNo 订单号
     * @param bankCard 银行卡信息
     * @return
     * @throws UnsupportedEncodingException
     */
    public Map getAuthenMap(String mchNo, String tracNo, BankCard bankCard) throws UnsupportedEncodingException {
        MchConfig config = CacheUtil.me().getMchConfigInfo(mchNo, "0");
        String product = "2";
        if(ToolUtil.isNotEmpty(config.getAuthenKey()) && "6EE8A8DC7BF08CE5F00EE1649E295385".equals(config.getAuthenKey())){
            product = "21";
        }
        Map<String, String> reqMap = new HashMap<>();
        reqMap.put("product", product);
        reqMap.put("mid", "181024007");
        reqMap.put("spOrder", tracNo);
        reqMap.put("txnDate", DateUtil.getCurrentDate());
        reqMap.put("cardNo", bankCard.getCardNo());
        reqMap.put("name", bankCard.getUserName());
        reqMap.put("certNo", bankCard.getCertNo());
        reqMap.put("phone", bankCard.getCardPhone());
        String str = createLinkStringByGet(reqMap);
        str += "&key=" + config.getAuthenKey();
        System.out.println("签名原串为：" + str);
        String sign = PayDigestUtil.md5(str, "utf-8");
        System.out.println("签名为：----" + sign);

        Map<String, String> reqMap2 = new HashMap<>();

        reqMap2.put("product", product);
        reqMap2.put("mid", "181024007");
        reqMap2.put("spOrder", tracNo);
        reqMap2.put("txnDate", DateUtil.getCurrentDate());
        reqMap2.put("cardNo", AESUtil.encrypt(bankCard.getCardNo(), config.getAuthenKey()));
        reqMap2.put("name", bankCard.getUserName());
        reqMap2.put("certNo", AESUtil.encrypt(bankCard.getCertNo(), config.getAuthenKey()));
        reqMap2.put("phone", bankCard.getCardPhone());
        reqMap2.put("sign", sign);

        return reqMap2;
    }

    /**
     * 字符串拼接 用于计算签名
     * @param params
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String createLinkStringByGet(Map<String, String> params) throws UnsupportedEncodingException {
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
//            value = URLEncoder.encode(value, "UTF-8");
            if (i == keys.size() - 1) {//拼接时，不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }

    /**
     * 记录鉴权明细
     *
     * @param bankCard 银行卡信息
     * @param mchNo    商户号
     * @param tracNo   订单号
     * @param remark   备注
     * @param status   认证状态
     * @param type     银行卡账户类型  0对公 1对私
     */
    private void insertAuthenticationDetail(BankCard bankCard, String mchNo, String tracNo, String remark, String status, String type, String fee) {
        // 记录鉴权明细
        AuthenticationDetail authenticationDetail = new AuthenticationDetail();
        //卡号
        authenticationDetail.setCardNo(bankCard.getCardNo());
        //子商户号
        authenticationDetail.setMchAccntNo(bankCard.getMchAccntNo());
        //商户号
        authenticationDetail.setMchNo(mchNo);
        //订单号/系统追踪号
        authenticationDetail.setOrderNo(tracNo);
        //鉴权类型 0对公 1对私
        authenticationDetail.setAuthenticationType(type);
        //鉴权时间
        authenticationDetail.setAuthenticationTime(new Date());
        //订单号（交易流水号）
        authenticationDetail.setOrderNo(bankCard.getTracNo());
        //持卡人姓名
        authenticationDetail.setUserName(bankCard.getUserName());
        //鉴权状态
        authenticationDetail.setStatus(status);
        //备注信息
        authenticationDetail.setRemark(remark);
        //创建时间
        authenticationDetail.setCreateTime(new Date());

        authenticationDetail.setFee(fee);
        authenticationDetailMapper.insert(authenticationDetail);
    }


    /**
     * 插入银行卡信息
     *
     * @param mchNo   商户号
     * @param orderNo 订单号
     * @param type    银行卡账户类型 0 对公 1对私
     * @param status  认证状态
     * @param remark  说明
     * @param amt     小额打款金额
     * @return
     */
    public Integer insertBankCard(BankCard card, String mchNo, String orderNo, String type, String status, String remark, int amt) {
        //插入对公账户信息,对公账户绑定状态为待认定
        card.setStatus(status);
        card.setRemark(remark);
        card.setTracNo(orderNo);
        card.setCreateTime(new Date());
        card.setUpdateTime(new Date());
        card.setCardAccntType(type);
        card.setMchNo(mchNo);
        card.setVerifyPhoneCode(String.valueOf(amt));
        return bankCardMapper.insert(card);
    }

    /**
     * @param
     * @Description: 插入事物表
     * @author wzc
     * @date 2018/7/24 17:19
     */
    public int insertDeposit(String mchNo, String outTransNo, String bizType) {
        DepositTransaction depositTransaction = new DepositTransaction();
        depositTransaction.setMchNo(mchNo);
        depositTransaction.setTransactionNo(UUID.randomUUID().toString().replace("-", "").toLowerCase());//事务号
        depositTransaction.setOutTransNo(outTransNo);//外部追踪号
        depositTransaction.setIdMchAccntChargeUp(0);
        depositTransaction.setCreateTime(new DateTime());//创建时间
        depositTransaction.setTransactionType(bizType);//事务类型
        depositTransaction.setUpdateTime(new DateTime());//更新时间
        //更新事务表
        int res = depositTransactionMapper.insert(depositTransaction);
        return res;
    }

    /**
     * 根据卡号，子商户账户号查询该卡是否已存在（包括成功及处理中）
     *
     * @param cardNo
     * @param mchAccntNo
     * @return
     */
    public BankCard checkBankCardInfo(String cardNo, String mchAccntNo) {
        BankCard bankCard = new BankCard();
        bankCard.setCardNo(cardNo);
        bankCard.setMchAccntNo(mchAccntNo);
        List<BankCard> list = bankCardMapper.select(bankCard);
        if (list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    /**
     * @param bankCard
     * @Description: 效验该子商户银行卡号是否存在
     * @author wzc
     * @date 2018/7/20 14:56
     */
    public Boolean isBankCard(BankCard bankCard) {
        BankCard bankCard1 = new BankCard();
        bankCard1.setMchAccntNo(bankCard.getMchAccntNo());
        bankCard1.setCardNo(bankCard.getCardNo());
//        bankCard1.setStatus("success");
        List<BankCard> list = bankCardMapper.selectBankCard(bankCard1);
        if (list.size() > 0) {
            return true;
        }
        return false;
    }

    /**
     * 根据订单号查询鉴权明细表
     *
     * @param mchNo
     * @param orderNo
     * @return
     */
    public AuthenticationDetail checkBankCrad(String mchNo, String orderNo) {
        AuthenticationDetail authenticationDetail = new AuthenticationDetail();
        authenticationDetail.setOrderNo(orderNo);
        authenticationDetail.setMchNo(mchNo);
        List<AuthenticationDetail> list = authenticationDetailMapper.select(authenticationDetail);
        if (list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 查询银行卡信息
     *
     * @param bankCard
     * @return
     */
    public List<BankCard> selectBankCard(BankCard bankCard) {
        return bankCardMapper.selectBankCard(bankCard);
    }

    /**
     * 查询未成功认证的数据
     *
     * @param
     * @return
     */
    public List<BankCard> selectNoSuccess() {
        return bankCardMapper.selectNoSuccess();
    }

    /**
     * 发送http请求
     * @param url
     * @param params
     * @return
     */
    public static String txn(String url, Map<String, String> params) {

        String json = null;
        try {
            IRequestService requestor = IRequestService.getInstance();
            json = (String) requestor.sendPost(url, params);
        } catch (IOException e) {
            json = ConfigureTest.TXN_ERROR_RESULT;
        } catch (Exception e) {
            json = ConfigureTest.SYS_ERROR_RESULT;
        }
        return json;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public BankCard bindPublicBankCard(String mchNo, BankCard bankCard) {
        BankCard cardResponse = new BankCard();
        String bankName = bankCard.getBankName();
        String orderNo = bankCard.getOrderNo(); //订单号
        String bankNum = ""; //行号

        //对公根据银行名称匹配卡宾
        List<BankCardBin> bankCardBinList = bankCardMapper.getBankCardBinByBankName(bankName);
        if (ToolUtil.isNotEmpty(bankCardBinList) && ToolUtil.isNotEmpty(bankCardBinList.get(0))) {
            bankNum = bankCardBinList.get(0).getBankNum();
        } else {
            cardResponse.setStatus("error");
            cardResponse.setErrmsg("未找到对应机构，不支持该银行！");
            return cardResponse;
        }

        int amt = (int) (Math.random() * 49 + 1); //向账户打款小于等于0.5元的随机金额

        String url = bankCard.getNotifyUrl(); //转账结果通知地址
        String cardNo = bankCard.getCardNo();
        String accountName = bankCard.getUserName(); //户名
        String isSameBank = "N"; //默认N 是否同行
        String isSameCity = "N"; //默认N 是否同城
        String city = "";//城市名称（非必输，有的客户获取不到城市名称）

        if ("兴业银行".equals(bankCard.getBankName())) {
            isSameBank = "Y"; //同行
        }
        if (ToolUtil.isNotEmpty(bankCard.getBankBranchName())) {
            city = bankCard.getBankBranchName().substring(0, 2);
        }

        WithdrawPay withdrawPay = null;
        try {
            //保存对公账户信息,绑定状态为待认定
            insertBankCard(bankCard, mchNo, orderNo, "0", "to_verify", "待认证", amt);
            //记录鉴权明细
            insertAuthenticationDetail(bankCard, mchNo, orderNo, "待认证", "to_verify", "0", String.valueOf(amt));

            //超级网银小额打款
            withdrawPay = WithdrawPayUtil.withdrawPay(mchNo, orderNo, isSameBank, isSameCity, cardNo, accountName,
                    bankName, bankNum, city, AmountUtil.convertCent2Dollar(String.valueOf(amt)), "1",
                    "1", "对公小额打款");

            //打款结果（失败/成功/未决  失败情况：不支持该业务，不存在付款账号，操作员不对，连接超时等）
            if ("FAIL".equals(withdrawPay.getXferprccode())) {
                cardResponse.setRemark(withdrawPay.getMessage());
                cardResponse.setStatus("fail");

                //删除银行卡信息
                BankCard card = new BankCard();
                card.setTracNo(bankCard.getOrderNo());
                bankCardMapper.deleteTracNo(card);

                //删除鉴权明细信息
                AuthenticationDetail authenticationDetail = new AuthenticationDetail();
                authenticationDetail.setOrderNo(orderNo);
                authenticationDetail.setMchNo(mchNo);
                authenticationDetailMapper.delete(authenticationDetail);
            } else if ("PAYOUT".equals(withdrawPay.getXferprccode())) {
                cardResponse.setRemark(withdrawPay.getMessage());
                cardResponse.setStatus("success");

                //插入对公账户信息,对公账户绑定状态为待认定
                insertBankCard(bankCard, mchNo, orderNo, "0", "to_handle", "打款成功", amt);
                //记录鉴权明细
                insertAuthenticationDetail(bankCard, mchNo, orderNo, "打款成功", "to_handle", "0", String.valueOf(amt));

               /* AuthenticationDetail authenticationDetail = new AuthenticationDetail();
                authenticationDetail.setStatus("success");//状态
                authenticationDetail.setRemark("打款成功");
                authenticationDetail.setMchNo(mchNo);//商户号
                authenticationDetail.setOrderNo(orderNo);//订单号/请求追踪号
                authenticationDetail.setMchAccntNo(bankCard.getMchAccntNo());
                authenticationDetailMapper.updateAuthenticationDetail(authenticationDetail);//更新鉴权明细信息*/
            } else if ("PENDING".equals(withdrawPay.getXferprccode())) {
                cardResponse.setRemark(withdrawPay.getMessage());
                cardResponse.setStatus("to_handle");
                //异步查询转账信息并五分钟一次通知客户转账成功，直到返回成功停止，等待客户调用往账接口
                try {
                    asyncTask.queryTransferStatus(bankCard.getMchAccntNo(), withdrawPay, mchNo, bankCard.getCardNo(), url, bankCard);
                } catch (Exception e) {
                    log.error("异步查询转账异常：{}", ToolUtil.getExceptionMsg(e));
                }
            }
            cardResponse.setMchAccntNo(bankCard.getMchAccntNo());
            cardResponse.setCardNo(bankCard.getCardNo());
        } catch (Exception e) {
            log.error("小额打款异常：{}", ToolUtil.getExceptionMsg(e));
            cardResponse.setErrmsg("打款失败");
            cardResponse.setStatus("error");
            return cardResponse;
        }
        return cardResponse;
    }

}
